Decimal 값 동등비교 누락 수정 계획 (Enum & Match, GlueSQL #1741)

NOTE

아직 착수 전 — 이번 주 일요일 작업 예정. Claude Code로 PR #2016 리뷰 코멘트를 확인하던 중, 함께 후보를 찾다가 발견한 이슈. translate.rs가 아닌 core/src/executor/evaluate 쪽이라 지금까지 작업한 것과 다른 코드 경로를 볼 수 있음 — (Rust) 열거형과 패턴 매칭(Enum & Match) - 핵심 개념 및 특징 정리 노트에서 배운 걸 실제 코드에서 확인하기 좋은 크기.

📌 개념

문제 — Decimal과 숫자 리터럴을 비교하면 항상 false

CREATE TABLE T (d DECIMAL);
INSERT INTO T VALUES (3.14);
SELECT * FROM T WHERE d = 3.14;

지금은 0 rows가 나옴 (원래는 1 row). Decimal 타입 컬럼을 리터럴 숫자와 =로 비교하는 모든 경우가 항상 거짓으로 평가됨.

원인 — value_eq_with_literal에 Value::Decimal 매치 암이 없음

core/src/executor/evaluate/evaluated/eq.rs의 value_eq_with_literal 함수(현재 153~190행 근처)가 Value와 Evaluated::Number(리터럴 숫자)를 매칭하는 곳인데, I8/I16/…/F64/Inet까지는 각각 매치 암이 있지만 Decimal만 빠져 있음 → _ => Tribool::from(false)로 폴백.

같은 파일 옆 cmp.rs(비교 연산자 </> 등을 담당)에는 이미 Decimal 처리가 있어서 =만 깨지고 </>는 정상 동작하는 비대칭 상태:

// core/src/executor/evaluate/evaluated/cmp.rs:69-70 (이미 있음, 참고용)
Value::Decimal(l) => {
    BigDecimal::new(l.mantissa().into(), i64::from(l.scale())).partial_cmp(number)
}

고쳐야 하는 pseudocode

// core/src/executor/evaluate/evaluated/eq.rs
// 1) 파일 상단 use에 BigDecimal 추가 (cmp.rs와 동일하게)
use bigdecimal::BigDecimal;
 
// 2) value_eq_with_literal의 match 안, 다른 Value::* 암들 사이에 추가
(Value::Decimal(l), Evaluated::Number(r)) => {
    let l = BigDecimal::new(l.mantissa().into(), i64::from(l.scale()));
    Tribool::from(l == *r)
}
  • r은 &BigDecimal이므로 *r로 역참조 후 비교 (또는 l == r.clone()이 아니라 &l == r 형태로 맞춰도 됨 — 실제 작성 시 cmp.rs의 partial_cmp(number) 호출부와 타입을 맞춰서 컴파일 에러 보고 조정)
  • eq()는 이미 Evaluated::Number, Evaluated::Value(_) 양쪽 순서를 최상위 evaluate_eq에서 대칭으로 처리하고 있어서((a, Value) | (Value, a) 패턴), value_eq_with_literal 한 곳만 고치면 3.14 = d, d = 3.14 양방향 다 해결됨

테스트 방법

  1. eq.rs 맨 아래 #[cfg(test)] mod tests에 이미 value_eq_with_literal_matches_previous_behavior 테스트가 있음 — 여기에 케이스 추가:
    assert_eq!(
        True,
        value_eq_with_literal(&Value::Decimal(Decimal::new(314, 2)), &num!("3.14"))
    );
    // 스케일이 다른 경우도 확인 (3.00 vs 3)
    assert_eq!(
        True,
        value_eq_with_literal(&Value::Decimal(Decimal::new(300, 2)), &num!("3"))
    );
  2. cargo test -p gluesql-core --lib 로 유닛 테스트 확인
  3. SQL 레벨 회귀도 하나 추가 고려: test-suite/fixtures/ 안에 decimal 관련 fixture가 있으면 거기에 WHERE d = 3.14 케이스 추가 (없으면 pkg/rust/tests 쪽에 작은 통합 테스트로 대체 가능 — 일요일에 fixture 유무부터 확인)
  4. 마무리로 cargo clippy -p gluesql-core --all-targets -- -D warnings, cargo fmt --all

배울 Rust 개념

  • 열거형과 패턴 매칭: Value/Evaluated처럼 variant가 많은 enum에서 match 암이 하나 빠지면 컴파일러가 못 잡아주는 경우(와일드카드 _ 폴백이 있을 때) vs 잡아주는 경우(와일드카드 없을 때)의 차이를 실제 코드로 체감
  • 튜플 패턴 매칭 (Value::Decimal(l), Evaluated::Number(r)) — 두 enum을 동시에 구조 분해

🔗 참고

관련 문서